package com.hitqz.robot.biz.listener;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.cache.Cache;
import com.hitqz.robot.api.business.dto.RobotControlButtonDto;
import com.hitqz.robot.api.business.enums.*;
import com.hitqz.robot.api.common.dto.info.ReleaseResourceDto;
import com.hitqz.robot.api.common.dto.robot.RobotGoTargetPosDto;
import com.hitqz.robot.api.common.dto.robot.RobotMoveDto;
import com.hitqz.robot.api.common.dto.tickets.CtrlTicketsEvent;
import com.hitqz.robot.api.common.dto.warning.AddWarningDto;
import com.hitqz.robot.api.common.entity.Robot;
import com.hitqz.robot.api.common.entity.RobotMapPos;
import com.hitqz.robot.api.common.entity.Tickets;
import com.hitqz.robot.api.common.entity.TicketsTask;
import com.hitqz.robot.api.dispatch.model.MessageDto;
import com.hitqz.robot.api.dispatch.protocol.ProtocolEnum;
import com.hitqz.robot.api.dispatch.protocol.dto.*;
import com.hitqz.robot.biz.business.dispatch.DispatchService;
import com.hitqz.robot.biz.config.LogTailer;
import com.hitqz.robot.biz.flow.util.LiteFlowUtil;
import com.hitqz.robot.biz.service.*;
import com.hitqz.robot.biz.util.CommonFileUtil;
import com.hitqz.robot.common.core.constant.CommonConstants;
import com.hitqz.robot.common.core.util.R;
import com.hitqz.robot.driver.parent.RobotDriverService;
import com.hitqz.robot.netty.service.Send2ServerService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Date;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.*;

/**
 * 监听来自服务端的消息，进行处理
 *
 * @author xupkun
 * @date 2024/6/13
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class SeverRobotEventListener {

    private final RobotDriverService robotDriverService;

    private final Send2ServerService send2ServerService;

    private final RobotService robotService;

    private final RobotMapService robotMapService;

    private final RobotMapPosService robotMapPosService;

    private final TicketsService ticketsService;

    private final TicketsTaskService ticketsTaskService;

    private final DispatchService dispatchService;

    private final PatrolResultService patrolResultService;

    private final BusinessService businessService;

    private final FormConfigService formConfigService;


    public static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10,
            1L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(20),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.DiscardPolicy());


    @Async("poolTaskExecutor")
    @EventListener
    public void handlerQuery(RobotCmdNavDto robotCmdNavDto) {
        log.info("调度系统要求导航状态 {}", JSONObject.toJSONString(robotCmdNavDto));
//        Cache<String, Integer> nextStep = SpringUtil.getBean("cmdNav");
//        if (robotCmdNavDto.getCmd() == CmdNavStatusEnum.NAV_PAUSE.getCode()) {
//            //log.info("导航暂停的话，进行标记上位机停的");
//            nextStep.put(robotCmdNavDto.getClientId(), robotCmdNavDto.getCmd());
//        } else {
//            //log.info("上位机控制导航恢复");
//            nextStep.put(robotCmdNavDto.getClientId(), -1);
//        }
        robotDriverService.navCmd(robotCmdNavDto.getCmd());
    }


    @EventListener
    @Async("poolTaskExecutor")
    public void handlerQuery(MessageDto dto) {
        if (dto.getCmd() == ProtocolEnum.ROBOT_SYNC_CMD.getCmd()) {
            RobotAsyncCmdDto robotAsyncCmdDto = JSONObject.parseObject(dto.getData(), RobotAsyncCmdDto.class);
            Robot robot = robotService.getDefaultRobot();
            if (robotAsyncCmdDto == null || robotAsyncCmdDto.getCmd() == null) {
                return;
            }
            if (robotAsyncCmdDto.getCmd() == 100) {
                // 同步地图
                robotMapService.syncMap();
            } else if (robotAsyncCmdDto.getCmd() == 101) {
                // 同步机器人消息
                robotService.syncRobot();
            } else if (robotAsyncCmdDto.getCmd() == 102) {
                // 同步点位
                robotMapPosService.syncMapPos();
            } else if (robotAsyncCmdDto.getCmd() == 103) {
                // 方便 后续改为 消息一起同步 上传
                for (FormConfigEnum formConfigEnum : FormConfigEnum.values()) {
                    if (formConfigEnum.getFormName().contains("tickets_params")
                            && formConfigEnum.getFormName().contains(robot.getRobotModel().toLowerCase())
                    ) {
                        log.info("上传动态表单内容：{},{}", formConfigEnum.getFormTitle(), formConfigEnum.getFormName());
                        formConfigService.syncFormMoal(formConfigEnum.getFormName());
                    }
                }
            }
            send2ServerService.commonReply(dto.getSn(), R.ok());
        }
    }


    @EventListener
    @Async("poolTaskExecutor")
    public void handlerQuery(CmdRobot2PosDto dto) {
        String posCode = dto.getPosCode();
        RobotMapPos robotMapPos = robotMapPosService.getOne(Wrappers.<RobotMapPos>lambdaQuery()
                .eq(RobotMapPos::getCode, posCode));
        log.info("上位机指定下发 {} ", posCode);
        dispatchService.go2TargetPosition(robotMapPos, false, false);
    }


    @Async("ctrlExecutor")
    @EventListener
    public void handlerQuery(RobotControlButtonDto dto) {
        try {
            if (dto == null || dto.getAction() == null) {
                log.warn("Invalid input: dto is null or action is null");
                return;
            }

            log.info("doAction====>>> {}", JSONObject.toJSONString(dto));
            String action = dto.getAction();
            switch (action) {
                case "goForward":
                    setSpeed("0.2", "0", "0");
                    break;
                case "backForward":
                    setSpeed("-0.2", "0", "0");
                    break;
                case "leftTranslate":
                    setSpeed("0", "0.2", "0");
                    break;
                case "rightTranslate":
                    setSpeed("0", "-0.2", "0");
                    break;
                case "left":
                    setSpeed("0", "0", "0.5");
                    break;
                case "right":
                    setSpeed("0", "0", "-0.5");
                    break;
                case "stop":
                case "cleanTask":
                    log.info("stop robot and task");
                    stopRobot();
                    // 提交路径为空，清空数据
                    //dispatchService.applyNavigationPath(robotService.getDefaultRobot(), new ArrayList<>());
                    break;
                case "charge":
                    //robotDriverService.cmdCharge(1);
                    log.info("充电命令");
                    businessService.goCharge(true, true);
                    break;
                case "cancelCharge":
                    robotDriverService.cmdCharge(0);
                    break;
                case "speedUp":
                    robotDriverService.changeWebControlSpeed(0.1f);
                    break;
                case "speedDown":
                    robotDriverService.changeWebControlSpeed(-0.1f);
                    break;
                case "powerOnOrOff":
                    robotDriverService.executeOtherCommand(RobotOtherCommandEnum.POWER_ON_OR_OFF, "");
                    break;
                case "gf-powerOnOrOff":
                    robotDriverService.executeOtherCommand(RobotOtherCommandEnum.SET_GF_IO, JSONObject.toJSONString(dto.getParam()));
                    break;
                case "gf-function":
                    robotDriverService.executeOtherCommand(RobotOtherCommandEnum.SET_GF_FUNCTION, JSONObject.toJSONString(dto.getParam()));
                    break;
                case "gf-relay":
                    robotDriverService.executeOtherCommand(RobotOtherCommandEnum.SET_GF_RELAY, JSONObject.toJSONString(dto.getParam()));
                    break;
                case "relocation":
                    log.info("relocation");
                    robotDriverService.executeOtherCommand(RobotOtherCommandEnum.RE_LOCATION, JSONObject.toJSONString(dto.getParam()));
                    break;
                case "rodController":
                    robotDriverService.executeOtherCommand(RobotOtherCommandEnum.ROD_CONTROLLER, JSONObject.toJSONString(dto.getParam()));
                    break;
                case "cancelController":
                    robotDriverService.executeOtherCommand(RobotOtherCommandEnum.CANCEL_CONTROLLER, JSONObject.toJSONString(dto.getParam()));
                    break;
                case "clearError":
                    robotDriverService.executeOtherCommand(RobotOtherCommandEnum.CLEAR_ERROR_MESSAGE, JSONObject.toJSONString(dto.getParam()));
                    break;
                case "jackingDown":
                    robotDriverService.executeOtherCommand(RobotOtherCommandEnum.JACKING_DOWN, JSONObject.toJSONString(dto.getParam()));
                    break;
                case "jackingUp":
                    robotDriverService.executeOtherCommand(RobotOtherCommandEnum.JACKING_UP, JSONObject.toJSONString(dto.getParam()));
                    break;
                case "turnBack":
                    // 返航回等待点，没有的话则返回充电点
                    businessService.cancelCharge();
                    break;
                default:
                    log.warn("Unknown action: {}", action);
                    break;
            }
            log.info("finish doAction ====>>> {}", JSONObject.toJSONString(dto));
        } catch (Exception e) {
            log.error("Error handling query: ", e);
        }

    }

    private void setSpeed(String linerSpeed, String linerSpeedY, String angularSpeed) {
        RobotMoveDto robotMoveDto = new RobotMoveDto();
        robotMoveDto.setLinerSpeed(linerSpeed);
        robotMoveDto.setLinerSpeedY(linerSpeedY);
        robotMoveDto.setAngularSpeed(angularSpeed);
        robotDriverService.setSpeed(robotMoveDto);
    }

    private void stopRobot() {
        Robot robot = robotService.getDefaultRobot();

        Integer ticketsId = LiteFlowUtil.ticketsId;
        log.info("robot is working, stop it." + ticketsId);
        if (ticketsId != null) {
            // 强制下线，防止出现异常
            robot.setDispatch(0);
            robotService.updateById(robot);
        }


        CtrlTicketsEvent ctrlTicketsEvent = new CtrlTicketsEvent();
        ctrlTicketsEvent.setType(CtrlTicketsEvent.TYPE_STOP);
        ctrlTicketsEvent.setMessage("上位机急停中断任务");
        ctrlTicketsEvent.setTicketsId(LiteFlowUtil.ticketsId);
        SpringUtil.getApplicationContext().publishEvent(ctrlTicketsEvent);
        // 暂停导航等功能
        log.info("取消导航");
        robotDriverService.motionAbort();

        RobotMoveDto robotMoveDto = new RobotMoveDto();
        robotMoveDto.setLinerSpeed("0");
        robotMoveDto.setAngle("0");
        robotMoveDto.setLinerSpeedY("0");
        robotDriverService.setSpeed(robotMoveDto);
        //机器人如果是前往充电中,则重置为空闲
        if (WorkStatusEnum.WAY_2_CHARGE.getCode().equals(robot.getWorkStatus())) {
            robot.setWorkStatus(WorkStatusEnum.FREE.getCode());
            robotService.updateById(robot);
        }

        //重置即将前往的目标点
        Cache<String, RobotMapPos> navTarget = SpringUtil.getBean("navTarget");
        navTarget.invalidate(robot.getRobotSn());

        //释放当前机器人占用的资源，取消当前机器人需要执行的避让行为
        ReleaseResourceDto releaseResourceDto = new ReleaseResourceDto();
        releaseResourceDto.setRobotSn(robot.getRobotSn());
        send2ServerService.send2ServerData(Integer.valueOf(ProtocolEnum.ROBOT_RELEASE_RESOURCE_CMD.getCmd()),
                JSONObject.toJSONString(releaseResourceDto));
    }


    @Async("poolTaskExecutor")
    @EventListener
    public void handlerRobotInfo(RobotDto dto) {
        //TODO 机器人控制
        //log.info("收到机器人的模型请求：" + dto.getChargeThreshold());
        Robot robot = robotService.getDefaultRobot();
        if (Objects.equals(robot.getRobotSn(), dto.getRobotSn())) {
            log.info("收到机器人的模型请求：" + dto.getChargeThreshold());
            BeanUtil.copyProperties(dto, robot, false);
            robotService.updateById(robot);
        }
    }

    @Async("poolTaskExecutor")
    @EventListener
    public void handlerNextStop(RobotNextStepDto dto) {
        log.info("收到执行下一步:{}--{}--{}", dto.getTaskCode(), dto.getClientId(), dto.getPosCode());
        Cache<String, RobotNextStepDto> nextStep = SpringUtil.getBean("nextStep");
        nextStep.put(dto.getTaskCode(), dto);
    }


    @Async("poolTaskExecutor")
    @EventListener
    public void handleTickets(RobotCmdTicketsDto dto) {
        Robot robot = robotService.getDefaultRobot();
        // 生成一个票数据
        Tickets tickets = new Tickets();
        if (StrUtil.isNotBlank(tickets.getTaskNo())) {
            tickets = ticketsService.getOne(Wrappers.<Tickets>lambdaQuery().eq(Tickets::getTaskNo, tickets.getTaskNo()));
            if (tickets == null) {
                tickets = new Tickets();
            }
        }
        tickets.setAllotRobotName(robot.getRobotName());
        tickets.setAllotRobotSn(robot.getRobotSn());
        tickets.setAllotStatus(TicketsStatusEnum.ALLOT_ALLOCATION.getCode());
        tickets.setStatus(TicketsStatusEnum.STATUS_NONE.getCode());
        tickets.setName(dto.getTaskName());
        tickets.setContent(dto.getTaskContent());
        tickets.setCreateTime(new Date());
        tickets.setUpdateTime(new Date());
        tickets.setTaskNo(dto.getTaskNo());
        if (tickets.getId() != null) {
            ticketsService.updateById(tickets);
        } else {
            ticketsService.save(tickets);
        }

        send2ServerService.commonReply((int) ProtocolEnum.ROBOT_CMD_TICKETS.getCmd(), R.ok());
        // 生成一个任务
        if (!ticketsTaskService.createTicket(dto.getTaskContent(), tickets)) {
            return;
        }
        Tickets finalTickets = tickets;
        threadPool.execute(() -> {
            try {
                R result = ticketsService.executeFlowChain(finalTickets.getId().intValue());
                log.info("开始执行：" + result.toString());
                if (result.getCode() == CommonConstants.FAIL) {
                    AddWarningDto addWarningDto = new AddWarningDto();
                    addWarningDto.setCode("0");
                    addWarningDto.setTitle("任务执行异常");
                    addWarningDto.setContent(result.getMsg());
                    addWarningDto.setSuggestion("请确认状态后,再启动任务。");
                    addWarningDto.setLevel(1);
                    SpringUtil.getApplicationContext().publishEvent(addWarningDto);
                }
            } catch (ExecutionException e) {
                throw new RuntimeException(e);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } catch (TimeoutException e) {
                throw new RuntimeException(e);
            }
        });
    }


    @EventListener
    @Async("poolTaskExecutor")
    public void listenerSysInfo(ModifySysInfoDto sysInfoDto) {
        log.info("修改系统消息:" + sysInfoDto.getSysName());

    }

    @EventListener
    @Async("poolTaskExecutor")
    public void listenerTickets(RobotTicketsDto dto) {
        log.info("收到工作票信息:" + dto.getServerId());
        Tickets tickets = ticketsService.getById(dto.getClientTicketsId());
        if (tickets == null) {
            return;
        }
        tickets.setServerId(dto.getServerId());
        ticketsService.updateById(tickets);
        for (RobotTicketsTaskDto taskDto : dto.getTickets()) {
            TicketsTask ticketsTask = ticketsTaskService.getById(taskDto.getClientTaskId());
            ticketsTask.setServerTaskId(taskDto.getServerId().longValue());
            ticketsTaskService.updateById(ticketsTask);
        }
    }


    @EventListener
    @Async("poolTaskExecutor")
    public void handlerQuery(ReportLogMessageDto dto) {
        log.info("收到日志开始推送消息:" + dto.getClientId());
        LogTailer.pushNow(dto.getClientId(), dto.getRandomId());
    }


    @EventListener
    @Async("poolTaskExecutor")
    public void handlerQuery(UploaderLogFileDto dto) {
        String filename = CommonFileUtil.zipLogFile();
        String serverFileName = patrolResultService.uploadFile(filename);
        send2ServerService.uploadLogFile(dto.getClientId(), serverFileName);
    }


    /**
     * 机器人逻辑上线/下线 默认是上线
     *
     * @param dto
     */
    @EventListener
    @Async("poolTaskExecutor")
    public void handlerQuery(RobotDispatchCmdDto dto) {
        Robot robot = robotService.getDefaultRobot();
        log.info("接收到命令,调度逻辑上下线:" + dto.getLogicOnline());
        robot.setDispatch(dto.getLogicOnline());
        robotService.updateById(robot);
        robotDriverService.changeDispatch(dto.getLogicOnline());
    }


}
